package Encoder;

/**
 * Title:        DCT<br>
 * Description:
 * DCT - A Java implementation of the Discreet Cosine Transform
 * This class incorporates quality scaling as implemented in the JPEG-6a
 * library.<br>
 * @author Randr
 * 
 */

public class DCT {
    /**
     * DCT Block Size - default 8.
     */
    private int size;

    /**
     * Image quality (0-100) - default 80 (good image / good compression).
     */
    private int quality;

    /**
     * Quantum.
     */
    private Object[] quantum;
    /**
     * Divisors.
     */
    private Object[] divisors;

    /**
     * Quantitization Matrix for luminace.
     */
    private int[] quantumLuminance;
    /**
     * Divisors matrix for Luminance.
     */
    private double[] divisorsLuminance;

    /**
     * Quantitization Matrix for chrominance.
     */
    private int[] quantumChrominance;
    /**
     * Divisors matrix for chrominance.
     */
    private double[] divisorsChrominance;

    /**
     * Constructs a new DCT object. Initializes the cosine transform matrix
     * these are used when computing the DCT and it's inverse. This also
     * initializes the run length counters and the ZigZag sequence. Note that
     * the image quality can be worse than 25 however the image will be
     * extemely pixelated, usually to a block size of size.
     *
     * @param quality The quality of the image (0 worst - 100 best)
     */
    public DCT(int quality)
    {
        this.size = 8;
        this.quality = quality;
        this.quantum = new Object[2];
        this.divisors = new Object[2];
        this.quantumLuminance     = new int[size*size];
        this.divisorsLuminance = new double[size*size];
        this.quantumChrominance     = new int[size*size];
        this.divisorsChrominance = new double[size*size];
        initMatrix(this.quality);
    }


    /**
     * Sets up the quantization matrix for luminance and chrominance using the quality parameter.
     *
     * @param quality The image quality.
     */
    private void initMatrix(int quality)
    {
        double[] aANscaleFactor = { 1.0, 1.387039845, 1.306562965, 1.175875602,
                                    1.0, 0.785694958, 0.541196100, 0.275899379};
        int i = 0;
        int j = 0;
        int index = 0;
        int imageQuality = 0;
        int temp = 0;

        // converting quality setting to that specified in the jpeg_quality_scaling
        // method in the IJG Jpeg-6a C libraries

        imageQuality = quality;
        if (imageQuality <= 0) {
                imageQuality = 1;
        }
        if (imageQuality > 100) {
                imageQuality = 100;
        }
        if (imageQuality < 50) {
                imageQuality = 5000 / imageQuality;
        }
        else {
                imageQuality = 200 - imageQuality * 2;
        }

        // Creating the luminance matrix

        quantumLuminance[0]=16;
        quantumLuminance[1]=11;
        quantumLuminance[2]=10;
        quantumLuminance[3]=16;
        quantumLuminance[4]=24;
        quantumLuminance[5]=40;
        quantumLuminance[6]=51;
        quantumLuminance[7]=61;
        quantumLuminance[8]=12;
        quantumLuminance[9]=12;
        quantumLuminance[10]=14;
        quantumLuminance[11]=19;
        quantumLuminance[12]=26;
        quantumLuminance[13]=58;
        quantumLuminance[14]=60;
        quantumLuminance[15]=55;
        quantumLuminance[16]=14;
        quantumLuminance[17]=13;
        quantumLuminance[18]=16;
        quantumLuminance[19]=24;
        quantumLuminance[20]=40;
        quantumLuminance[21]=57;
        quantumLuminance[22]=69;
        quantumLuminance[23]=56;
        quantumLuminance[24]=14;
        quantumLuminance[25]=17;
        quantumLuminance[26]=22;
        quantumLuminance[27]=29;
        quantumLuminance[28]=51;
        quantumLuminance[29]=87;
        quantumLuminance[30]=80;
        quantumLuminance[31]=62;
        quantumLuminance[32]=18;
        quantumLuminance[33]=22;
        quantumLuminance[34]=37;
        quantumLuminance[35]=56;
        quantumLuminance[36]=68;
        quantumLuminance[37]=109;
        quantumLuminance[38]=103;
        quantumLuminance[39]=77;
        quantumLuminance[40]=24;
        quantumLuminance[41]=35;
        quantumLuminance[42]=55;
        quantumLuminance[43]=64;
        quantumLuminance[44]=81;
        quantumLuminance[45]=104;
        quantumLuminance[46]=113;
        quantumLuminance[47]=92;
        quantumLuminance[48]=49;
        quantumLuminance[49]=64;
        quantumLuminance[50]=78;
        quantumLuminance[51]=87;
        quantumLuminance[52]=103;
        quantumLuminance[53]=121;
        quantumLuminance[54]=120;
        quantumLuminance[55]=101;
        quantumLuminance[56]=72;
        quantumLuminance[57]=92;
        quantumLuminance[58]=95;
        quantumLuminance[59]=98;
        quantumLuminance[60]=112;
        quantumLuminance[61]=100;
        quantumLuminance[62]=103;
        quantumLuminance[63]=99;

        for (j = 0; j < 64; j++) {
                temp = (quantumLuminance[j] * imageQuality + 50) / 100;
                if ( temp <= 0) {
                  temp = 1;
                }
                if (temp > 255) {
                  temp = 255;
                }
                quantumLuminance[j] = temp;
        }
        index = 0;
        for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
        // The divisors for the LL&M method (the slow integer method used in
        // jpeg 6a library).  This method is currently (04/04/98) incompletely
        // implemented.
        //                        divisorsLuminance[index] = ((double) quantumLuminance[index]) << 3;
        // The divisors for the AAN method (the float method used in jpeg 6a library.
                        divisorsLuminance[index] = (double) ((double)1.0/((double) quantumLuminance[index] * aANscaleFactor[i] * aANscaleFactor[j] * (double) 8.0));
                        index++;
                }
        }


        // Creating the chrominance matrix

        quantumChrominance[0]=17;
        quantumChrominance[1]=18;
        quantumChrominance[2]=24;
        quantumChrominance[3]=47;
        quantumChrominance[4]=99;
        quantumChrominance[5]=99;
        quantumChrominance[6]=99;
        quantumChrominance[7]=99;
        quantumChrominance[8]=18;
        quantumChrominance[9]=21;
        quantumChrominance[10]=26;
        quantumChrominance[11]=66;
        quantumChrominance[12]=99;
        quantumChrominance[13]=99;
        quantumChrominance[14]=99;
        quantumChrominance[15]=99;
        quantumChrominance[16]=24;
        quantumChrominance[17]=26;
        quantumChrominance[18]=56;
        quantumChrominance[19]=99;
        quantumChrominance[20]=99;
        quantumChrominance[21]=99;
        quantumChrominance[22]=99;
        quantumChrominance[23]=99;
        quantumChrominance[24]=47;
        quantumChrominance[25]=66;
        quantumChrominance[26]=99;
        quantumChrominance[27]=99;
        quantumChrominance[28]=99;
        quantumChrominance[29]=99;
        quantumChrominance[30]=99;
        quantumChrominance[31]=99;
        quantumChrominance[32]=99;
        quantumChrominance[33]=99;
        quantumChrominance[34]=99;
        quantumChrominance[35]=99;
        quantumChrominance[36]=99;
        quantumChrominance[37]=99;
        quantumChrominance[38]=99;
        quantumChrominance[39]=99;
        quantumChrominance[40]=99;
        quantumChrominance[41]=99;
        quantumChrominance[42]=99;
        quantumChrominance[43]=99;
        quantumChrominance[44]=99;
        quantumChrominance[45]=99;
        quantumChrominance[46]=99;
        quantumChrominance[47]=99;
        quantumChrominance[48]=99;
        quantumChrominance[49]=99;
        quantumChrominance[50]=99;
        quantumChrominance[51]=99;
        quantumChrominance[52]=99;
        quantumChrominance[53]=99;
        quantumChrominance[54]=99;
        quantumChrominance[55]=99;
        quantumChrominance[56]=99;
        quantumChrominance[57]=99;
        quantumChrominance[58]=99;
        quantumChrominance[59]=99;
        quantumChrominance[60]=99;
        quantumChrominance[61]=99;
        quantumChrominance[62]=99;
        quantumChrominance[63]=99;

        for (j = 0; j < 64; j++)
        {
                temp = (quantumChrominance[j] * imageQuality + 50) / 100;
                if ( temp <= 0) {
                  temp = 1;
                }
                if (temp >= 255) {
                  temp = 255;
                }
                quantumChrominance[j] = temp;
        }
        index = 0;
        for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
        // The divisors for the LL&M method (the slow integer method used in
        // jpeg 6a library).  This method is currently (04/04/98) incompletely
        // implemented.
        //                        divisorsChrominance[index] = ((double) quantumChrominance[index]) << 3;
        // The divisors for the AAN method (the float method used in jpeg 6a library.
                        divisorsChrominance[index] = (double) ((double)1.0/((double) quantumChrominance[index] * aANscaleFactor[i] * aANscaleFactor[j] * (double)8.0));
                        index++;
                }
        }

        // quantum and divisors are objects used to hold the appropriate matices

        quantum[0] = quantumLuminance;
        divisors[0] = divisorsLuminance;
        quantum[1] = quantumChrominance;
        divisors[1] = divisorsChrominance;

    }

    /**
     * Returns the value of the quantum table for the specified index.
     *
     * @param i the index of the quantum table.
     * @return the value from the quantum table.
     */
    Object getQuantum(int i) {
      return quantum[i];
    }

    /**
     * Preforms a DCT on a block of image data using the AAN method
     * as implemented in the IJG Jpeg-6a library.
     *
     * @param input The input data.
     * @return the DTC result.
     */
    public double[][] forwardDCT(float[][] input)
    {
        double[][] output = new double[size][size];
        double tmp0 = 0;
        double tmp1 = 0;
        double tmp2 = 0;
        double tmp3 = 0;
        double tmp4 = 0;
        double tmp5 = 0;
        double tmp6 = 0;
        double tmp7 = 0;
        double tmp10 = 0;
        double tmp11 = 0;
        double tmp12 = 0;
        double tmp13 = 0;
        double z1 = 0;
        double z2 = 0;
        double z3 = 0;
        double z4 = 0;
        double z5 = 0;
        double z11 = 0;
        double z13 = 0;
        int i = 0;
        int j = 0;

        // Subtracts 128 from the input values
        for (i = 0; i < 8; i++) {
                for(j = 0; j < 8; j++) {
                        output[i][j] = ((double)input[i][j] - (double)128.0);
                }
        }

        for (i = 0; i < 8; i++) {
                tmp0 = output[i][0] + output[i][7];
                tmp7 = output[i][0] - output[i][7];
                tmp1 = output[i][1] + output[i][6];
                tmp6 = output[i][1] - output[i][6];
                tmp2 = output[i][2] + output[i][5];
                tmp5 = output[i][2] - output[i][5];
                tmp3 = output[i][3] + output[i][4];
                tmp4 = output[i][3] - output[i][4];

                tmp10 = tmp0 + tmp3;
                tmp13 = tmp0 - tmp3;
                tmp11 = tmp1 + tmp2;
                tmp12 = tmp1 - tmp2;

                output[i][0] = tmp10 + tmp11;
                output[i][4] = tmp10 - tmp11;

                z1 = (tmp12 + tmp13) * (double) 0.707106781;
                output[i][2] = tmp13 + z1;
                output[i][6] = tmp13 - z1;

                tmp10 = tmp4 + tmp5;
                tmp11 = tmp5 + tmp6;
                tmp12 = tmp6 + tmp7;

                z5 = (tmp10 - tmp12) * (double) 0.382683433;
                z2 = ((double) 0.541196100) * tmp10 + z5;
                z4 = ((double) 1.306562965) * tmp12 + z5;
                z3 = tmp11 * ((double) 0.707106781);

                z11 = tmp7 + z3;
                z13 = tmp7 - z3;

                output[i][5] = z13 + z2;
                output[i][3] = z13 - z2;
                output[i][1] = z11 + z4;
                output[i][7] = z11 - z4;
        }

        for (i = 0; i < 8; i++) {
                tmp0 = output[0][i] + output[7][i];
                tmp7 = output[0][i] - output[7][i];
                tmp1 = output[1][i] + output[6][i];
                tmp6 = output[1][i] - output[6][i];
                tmp2 = output[2][i] + output[5][i];
                tmp5 = output[2][i] - output[5][i];
                tmp3 = output[3][i] + output[4][i];
                tmp4 = output[3][i] - output[4][i];

                tmp10 = tmp0 + tmp3;
                tmp13 = tmp0 - tmp3;
                tmp11 = tmp1 + tmp2;
                tmp12 = tmp1 - tmp2;

                output[0][i] = tmp10 + tmp11;
                output[4][i] = tmp10 - tmp11;

                z1 = (tmp12 + tmp13) * (double) 0.707106781;
                output[2][i] = tmp13 + z1;
                output[6][i] = tmp13 - z1;

                tmp10 = tmp4 + tmp5;
                tmp11 = tmp5 + tmp6;
                tmp12 = tmp6 + tmp7;

                z5 = (tmp10 - tmp12) * (double) 0.382683433;
                z2 = ((double) 0.541196100) * tmp10 + z5;
                z4 = ((double) 1.306562965) * tmp12 + z5;
                z3 = tmp11 * ((double) 0.707106781);

                z11 = tmp7 + z3;
                z13 = tmp7 - z3;

                output[5][i] = z13 + z2;
                output[3][i] = z13 - z2;
                output[1][i] = z11 + z4;
                output[7][i] = z11 - z4;
        }

        return output;
    }

    /**
     * Quantitizes data and rounds it to the nearest integer.
     *
     * @param inputData The input data.
     * @param code The code.
     * @return The array with the rounds to the nearest integer.
     */
    public int[] quantizeBlock(double[][] inputData, int code)
    {
        int[] outputData = new int[size*size];
        int i = 0;
        int j = 0;
        int index = 0;
        for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                        outputData[index] = (int)(Math.round(inputData[i][j] * (((double[]) (divisors[code]))[index])));
                        index++;
                }
        }
        return outputData;
    }

}

